跳到主要内容

django ORM

[toc]

5.ORM

5.1 ORM简介

  • MVC或者MVC框架中包括一个重要的部分,就是ORM,它实现了数据模型与数据库的解耦,即数据模型的设计不需要依赖于特定的数据库,通过简单的配置就可以轻松更换数据库,这极大的减轻了开发人员的工作量,不需要面对因数据库变更而导致的无效劳动
  • ORM是“对象-关系-映射”的简称。(Object Relational Mapping,简称ORM)
  • ORM执行的过程
    • 类对象--->sql--->pymysql--->mysql服务端--->磁盘,orm其实就是将类对象的语法翻译成sql语句的一个引擎

ORM与原生SQL对比

iShot_2024-08-30_14.19.30

5.2 ORM操作

5.2.1 ORM连接mysql

这里先来一个orm连接mysql并创建一张表的示例

第一步、修改settings配置文件,配置mysql相关信息

#先注释默认项
# DATABASES = {
# 'default': {
# 'ENGINE': 'django.db.backends.sqlite3',
# 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
# }
# }


DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': '数据库名',
'HOST': '数据库IP',
'PORT': '数据库端口',
'USER': '用户',
'PASSWORD': '密码'
}
}

第二步、在项目目录下的同名目录下的__init__.py文件中写上以下内容,来指定pymysql作为连接客户端

import pymysql
pymysql.install_as_MySQLdb()

第三步、在应用程序目录下面的models.py文件中写对应的类,这里为创建一张表

from django.db import models 

#这里表示创建一个userinfo表,需要注意的是,django在创建表的时候会把表名小写并重命名为 应用程序_userinfo
class UserInfo(models.Model):
id = models.AutoField(primary_key=True)
username = models.CharField(max_length=10)
password = models.CharField(max_length=32)

第四步: 执行数据库同步指令,在终端中执行,执行的路径是django项目下

#在migrations文件夹下面生成记录文件
python3 manage.py makemigrations

#执行记录文件
python3 manage.py migrate

可以看到,我们自定义的表已经创建完成了,但是发现多了好多其他的表,那这些表是从哪来的呢,这些是django从settings文件中循环读取INSTALLED_APPS下的所有子项而默认创建的表

iShot_2024-08-30_14.22.32

表结构

iShot_2024-08-30_14.29.43

5.2.2 单表操作

5.2.2.1 增

应用程序的models.py文件中已经定义了创建一张表的语句

urls文件

from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^index/', views.index),
]

views文件

from django.shortcuts import render,HttpResponse
from app01 import models

#方式1
def index(request):
obj = models.UserInfo(
username='小明',
password='123'
)
obj.save()
return HttpResponse('ok')


#方式2
def index(request):
models.UserInfo.objects.create(
username='小颖',
password='666'
)
return HttpResponse('ok')

接下来启动django项目,浏览器访问我们指定的127.0.0.1:8000/index

执行以上任意一种方式都可以插入数据

iShot_2024-08-30_14.32.20

5.2.2.2 删

views文件

def index(request):
#删除UserInfo表中id值为1的值
models.UserInfo.objects.filter(id=1).delete()
return HttpResponse('ok')

浏览器访问127.0.0.1/index即可成功执行语句

iShot_2024-08-30_14.35.09

可以看到,id为1的字段已经被删除

iShot_2024-08-30_14.36.28

5.2.2.3 改

views文件

from django.shortcuts import render,HttpResponse
from app01 import models
# Create your views here.

def index(request):
# 方式1
models.UserInfo.objects.filter(id=2).update(
username='abc',
password='abc',
)

# 方式2
# obj = models.UserInfo.objects.filter(id=2)[0]
# obj.username = 'ggg'
# obj.password = 'ggg'
# obj.save()

return HttpResponse('ok')

浏览器访问127.0.0.1/index

可以看到修改已经生效

iShot_2024-08-30_14.40.34

批量创建

views文件

from django.shortcuts import render,HttpResponse
from app01 import models
# Create your views here.

def index(request):
list_obj = []
for i in range(6):
obj = models.UserInfo(
username='name%s' %i,
password='pwd%s' %i,
)
list_obj.append(obj)
print(list_obj)
models.UserInfo.objects.bulk_create(list_obj)

return HttpResponse('ok')

print(list_obj)返回的结果如下

[<UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>, <UserInfo: UserInfo object>]

浏览器访问127.0.0.1/index

可以看到已经批量插入了数据

iShot_2024-08-30_14.42.24

update_or_create 有就更新,没有就创建

from django.shortcuts import render,HttpResponse
from app01 import models
# Create your views here.

def index(request):
a,b = models.UserInfo.objects.update_or_create(
username='name0',
defaults={
'id': 20,
'password': 'abcdef',
}
)

print(a) #当前更新后的model对象,或者是你新增的记录的model对象
print(b) #新增就是True,查询就False

return HttpResponse('ok')


#a返回的结果
UserInfo object

#b返回的结果
True

iShot_2024-08-30_14.43.45

5.2.2.4 查

最简单的查询

from django.shortcuts import render,HttpResponse
from app01 import models
# Create your views here.

def index(request):
ret = models.UserInfo.objects.filter(id=1)
print(ret) # <QuerySet [<UserInfo: UserInfo object>]> -- []
obj = ret[0]
print(obj.id, obj.username)

return HttpResponse('ok')

表app01_userinfo内容如下

iShot_2024-08-30_14.45.50

print(ret)结果如下
<QuerySet [<UserInfo: UserInfo object>]>

print(obj.id, obj.username)执行后结果如下
1 aaa
查询非常重要的13种方法
1 all() 查询所有结果,结果是queryset类型

views文件

from django.shortcuts import render,HttpResponse
from app01 import models
# Create your views here.

def index(request):
ret = models.UserInfo.objects.all()
print(ret)

return HttpResponse('ok')

#print(ret)结果如下
<QuerySet [<UserInfo: UserInfo object>, <UserInfo: UserInfo object>]>

现在我们想看到的是数据库中的数据,需要在models文件中我们创建的UserInfo中写一个类的属性
class UserInfo(models.Model):
id = models.AutoField(primary_key=True)
username = models.CharField(max_length=10)
password = models.CharField(max_length=32)

#在models文件中写上这个属性后就能在all()方法中返回数据而不是对戏那个
def __str__(self):
return self.username


#结果如下
<QuerySet [<UserInfo: aaa>, <UserInfo: bbb>]>
2 get() 只能返回一个数据,没有或者数据多就报错,不是queryset类型,是行记录对象

例如,查询一个表中密码为aaa的数据,如果这个表中只有一个用户的密码是aaa,则可以查询成功,如果这个表中有多个用户的密码是aaa这个时候就会报错并且这个表中如果没有用户的密码是aaa同样会报错

from django.shortcuts import render,HttpResponse
from app01 import models
# Create your views here.

def index(request):
ret = models.UserInfo.objects.get(password='aaa')
print(ret)
return HttpResponse('ok')

表app01_userinfo内容如下

iShot_2024-08-30_14.46.35

浏览器访问127.0.0.1/index

此时返回的结果是可以成功查询的

现在修改表内容如下

iShot_2024-08-30_14.48.07

再次执行以上代码,查询表中密码为aaa的数据,此时会报错

iShot_2024-08-30_14.49.07

修改代码为查询表中密码为bbb的数据,同样会报错

iShot_2024-08-30_14.50.19

get两个报错,但是get请求返回的结果是model对象

#1.UserInfo matching query does not exist. 没有查到的报错
#2 get() returned more than one UserInfo -- it returned 11! 结果多了,不行!
3 filter() 查询数据,返回值是queryset类型

views文件

from django.shortcuts import render,HttpResponse
from app01 import models
# Create your views here.

def index(request):
ret = models.UserInfo.objects.filter(password='bbb')
print(ret)
return HttpResponse('ok')

filter查询表中密码为bbb的数据,虽然表中结果没有,但是不会像get报错

print(ret)结果如下

<QuerySet []>

filter查询表中密码为aaa的数据,表中密码为aaa的数据有2条,filter不会像get一样报错

<QuerySet [<UserInfo: aaa>, <UserInfo: bbb>]>
4 exclude() 排除,返回值是queryset类型

现有app01_book表如下

iShot_2024-08-30_14.53.02

查询所有数据,排除bid为4的数据

urls文件

from django.conf.urls import url,include
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^index/', views.index),
]

views文件

from django.shortcuts import render,reverse,redirect,HttpResponse
from app01 import models

def index(request):
#查询bid不为4的数据,这里还可以写多个排除条件,以逗号分隔
ret = models.Book.objects.exclude(bid=4)
print(ret)
return HttpResponse('ok')

models文件

class Book(models.Model):
bid = models.AutoField(primary_key=True)
bname = models.CharField(max_length=20)
price = models.DecimalField(max_digits=6,decimal_places=2)
publish_date = models.DateField(auto_now_add=False)
publish = models.CharField(max_length=20)

def __str__(self):
return self.bname

浏览器访问127.0.0.1/index,返回结果如下,bid为4的隔壁老王的故事没有返回

<QuerySet [<Book: 1-Linux从入门到放弃>, <Book: 2-python从入门到放弃>, <Book: 3-mysql之从删库到跑路>, <Book: 5-放开那个女孩>, <Book: 6-成为富翁的秘密>]>
5 order_by() queryset类型的数据来调用,对查询结果排序,默认是按照id来升序排列的,返回值是queryset类型

按照bid倒序排序,在bid前加个-号即可

viwes文件

def index(request):
ret = models.Book.objects.all().order_by('-bid')
print(ret)
return HttpResponse('ok')

浏览器反问127.0.0.1/index,返回结果如下,结果是倒序

<QuerySet [<Book: 6-成为富翁的秘密>, <Book: 5-放开那个女孩>, <Book: 4-隔壁老王的故事>, <Book: 3-mysql之从删库到跑路>, <Book: 2-python从入门到放弃>, <Book: 1-Linux从入门到放弃>]>

order_by()中加多个条件说明

#这段代码的意思是按照id倒叙排序,然后再按照id相同的数据进行价格升序排序
models.Test.objects.all().order_by('-id','money')

现有表app01_test如下

iShot_2024-08-30_14.54.12

先按照id进行倒叙排序

ret = models.Test.objects.all().order_by('-id')
print(ret.values())

返回结果如下

<QuerySet [{'id': 3, 'name': '3-哈哈', 'money': 8}, {'id': 3, 'name': '3-大强', 'money': 80}, {'id': 3, 'name': '3-呵呵', 'money': 800}, {'id': 2, 'name': '2-小颖', 'money': 100}, {'id': 1, 'name': '1-小明', 'money': 10}]>

再加一个条件进行排序,先根据id相同的进行倒序排序,然后再根据money升序排序

ret = models.Test.objects.order_by('-id','money')
print(ret.values())

返回结果如下

<QuerySet [{'id': 3, 'name': '3-大强', 'money': 80}, {'id': 3, 'name': '3-呵呵', 'money': 800}, {'id': 3, 'name': '3-哈哈', 'money': 8}, {'id': 2, 'name': '2-小颖', 'money': 100}, {'id': 1, 'name': '1-小明', 'money': 10}]>
6 reverse() queryset类型的数据来调用,对查询结果反向排序,返回值还是queryset类型

以例5中app01_test表为例

⚠️直接进行reverse进行反向排序是不生效的,必须先进行分组

ret = models.Test.objects.all().reverse()

返回结果如下,不分组进行反向排序是不生效的

<QuerySet [{'id': 1, 'name': '1-小明', 'money': 10}, {'id': 2, 'name': '2-小颖', 'money': 100}, {'id': 3, 'name': '3-大强', 'money': 80}, {'id': 3, 'name': '3-呵呵', 'money': 800}, {'id': 3, 'name': '3-哈哈', 'money': 8}]>

先进行分组,然后再进行反向排序才能生效

ret = models.Test.objects.all().order_by('id').reverse()

返回结果如下

<QuerySet [{'id': 3, 'name': '3-大强', 'money': 80}, {'id': 3, 'name': '3-呵呵', 'money': 800}, {'id': 3, 'name': '3-哈哈', 'money': 8}, {'id': 2, 'name': '2-小颖', 'money': 100}, {'id': 1, 'name': '1-小明', 'money': 10}]>
7 count() queryset类型的数据来调用,返回数据库中匹配查询(QuerySet)的对象数量

以例5中app01_test表为例

ret = models.Test.objects.all().count()
print(ret)

返回结果为5,因为app01_test表中有5条记录

8 first() queryset类型的数据来调用,返回第一条记录,得到的都是model对象,不是queryset

以例5中app01_test表为例

ret = models.Test.objects.all().first()
print(ret)


#models文件表的类中需要写如下代码
class Test(models.Model):
id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=10)
money = models.IntegerField()

def __str__(self):
return self.name

返回结果为1-小明

9 last() queryset类型的数据来调用,返回最后一条记录,得到的是model对象

以例5中app01_test表为例

ret = models.Test.objects.all().last()
print(ret)


#models文件表的类中需要写如下代码
class Test(models.Model):
id = models.IntegerField(primary_key=True)
name = models.CharField(max_length=10)
money = models.IntegerField()

def __str__(self):
return self.name

查询时遇到的一个问题,使用last方法查询一个没有设置主键的表时,只能返回id相同的记录中的第一个id的记录

例如,一张表中id字段如下:1、2、3、3、3,查询的返回的结果是id=3的第一条记录,即2后边的第一个3

iShot_2024-08-30_14.55.13

此表中没有设置主键,只能返回3-大强,而理论上是3-哈哈

10 exists() queryset类型的数据来调用,如果QuerySet包含数据,就返回True,否则返回False

空的queryset类型数据也有布尔值True和False,但是一般不用它来判断数据库里面是不是有数据,如果有大量的数据,你用它来判断,那么就需要查询出所有的数据,效率太差了,用count或者exits 例:all_books = models.Book.objects.all().exists() #翻译成的sql是SELECT (1) AS a FROM app01_book LIMIT 1,就是通过limit 1,取一条来看看是不是有数据

以例5中app01_test表为例

test表中没有id=5的数据,因此会返回False

ret = models.Test.objects.filter(id=5).exists()
11 values() 用的比较多,queryset类型的数据来调用,返回一ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列model的实例化对象,而是一个可迭代的字典序列,只要是返回的queryset类型,就可以继续连续调用queryset类型的其他的查找方法,其他方法也是一样的。

以例5中app01_test表为例

 ret = models.UserInfo.objects.all().values('id','name')  queryset调用          

ret = models.UserInfo.objects.values('id','name') objects调用--对所有数据进行取值

返回结果如下

<QuerySet [{'id': 1, 'name': '1-小明'}, {'id': 2, 'name': '2-小颖'}, {'id': 3, 'name': '3-大强'}, {'id': 3, 'name': '3-呵呵'}, {'id': 3, 'name': '3-哈哈'}]>
12 values_list 它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列

以例5中app01_test表为例

ret = models.Test.objects.all().values_list('id','name')

返回结果如下

<QuerySet [(1, '1-小明'), (2, '2-小颖'), (3, '3-大强'), (3, '3-呵呵'), (3, '3-哈哈')]>      
13 distinct() values和values_list得到的queryset类型的数据来调用,从返回结果中剔除重复纪录

以例5中app01_test表为例

 ret = models.UserInfo.objects.all().values('id','name').distinct()               
ret = models.UserInfo.objects.values('id','name').distinct()
基于双下划线的模糊查询

表app01_book内容如下

iShot_2024-08-30_14.58.16

#price值等于这三个里面的任意一个的对象
Book.objects.filter(price__in=[10,33,9999])

#大于,大于等于是price__gte=100,别写price>100,这种参数不支持
Book.objects.filter(price__gt=100)

#小于,小于等于是price__lte=100,别写price<100,这种参数不支持
Book.objects.filter(price__lt=100)

#sql的between and,大于等于100,小于等于10000
Book.objects.filter(price__range=[100,10000])

#bname值中包含python的
Book.objects.filter(bname__contains="python")

#不区分大小写,在前边加一个i就是不区分大小写,其余用法相同
Book.objects.filter(bname__icontains="python")

#以什么开头,istartswith 不区分大小写
Book.objects.filter(bname__startswith="py")

#查询publish_date是2020年的
Book.objects.filter(publish_date__year=2020)

查询示例

1 查询某某出版社出版过的价格大于100的书籍
ret = models.Book.objects.filter(price__gt=100,publish='老王出版社').values('bname')

2 查询201811月出版的所有以Li开头的书籍名称
ret = models.Book.objects.filter(bname__startswith='Linux',publish_date__year=2018,publish_date__month=11).values('bname')

3 查询价格为88,100或者9999的所有书籍名称及其出版社名称
ret = models.Book.objects.filter(price__in=[88,100,9999]).values('bname','publish')

4 查询价格在80100之间的所有书籍名称及其价格
ret = models.Book.objects.filter(price__range=[80,100]).values('bname','price')

5 查询所有老王出版社出版的书籍的价格(从高到低排序,去重)
ret = models.Book.objects.filter(publish='老王出版社').values('price').order_by('-price').distinct()

5.2.3 多表操作

表关系及字段设计

iShot_2024-08-30_15.00.38

urls文件

from django.conf.urls import url
from django.contrib import admin
from app01 import views

urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^query/', views.query),
]

settings文件配置数据库连接

DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'orm01',
'HOST': '127.0.0.1',
'PORT': '3306',
'USER': 'root',
'PASSWORD': 'xxx'
}
}

models文件

from django.db import models

# Create your models here.

class Author(models.Model):
name=models.CharField(max_length=32)
age=models.IntegerField()
ad=models.OneToOneField(to="AuthorDetail",to_field="id",on_delete=models.CASCADE)
# to_field="id" 可以不写,默认找主键
# to="AuthorDetail", to=可以不用写
# models.SET_NULL 置空
# on_delete=models.CASCADE 默认是级联删除,想做级联更新,直接去数据库修改表结构

class AuthorDetail(models.Model):
birthday=models.DateField()
telephone=models.CharField(max_length=16)
addr=models.CharField(max_length=64)


class Publish(models.Model):
name=models.CharField(max_length=32)
city=models.CharField(max_length=32)

class Book(models.Model):
title = models.CharField(max_length=32)
publishDate=models.DateField()
price=models.DecimalField(max_digits=5,decimal_places=2)
publishs=models.ForeignKey(to="Publish",to_field="id",on_delete=models.CASCADE)

#这行代码的意思是生成第3张表关联book表和author表
authors=models.ManyToManyField(to='Author',)

⚠️如果AuthorDetail写在Author的上边,则ad=models.OneToOneField(to="AuthorDetail"中的AuthorDetail可以不加引号

⚠️django1.x版本on_delete=models.CASCADE可以不写,默认是级连删除,如果想做级连更新,直接到数据库中操作,但是django2.x必须写级连删除

⚠️OneToOneField是一对一 ForeignKey是一对多 ManyToManyField是多对多

django项目下同名目录__init__.py文件

import pymysql
pymysql.install_as_MySQLdb()

相关配置文件配置完成后开始同步数据库

⚠️makemigrations执行后必须执行migrate,否则会出问题

//在django项目下
#在migrations目录下面生成记录文件
python3 manage.py makemigrations

#执行记录文件
python3 manage.py migrate

关于只执行makemigrations不执行migrate可能出现的问题

1.当执行makemigrations后,会在django项目下的makemigrations目录中创建一个0001_initial.py,这个文件记录的是models文件中的操作(创建表、删除表),当执行migrate后会执行数据库同步操作,同时在数据库中django会自动创建一张django_migrations表,这个表记录的是models文件中的操作

2.如果只执行了makemigrations而不执行migrate,当修改了models文件再次重复执行makemigrations,django会从数据库中的django_migrations查看0001_initial是否执行过,而此时0001_initial已经执行过了,因此会出现修改了models文件执行migrate提示没有改动的问题

同步后的数据库,可以看到app01_book_authors这张表是orm自动帮我们创建的

iShot_2024-08-30_15.02.05

5.2.3.1 增

views文件

一对一,作者表和作者详细信息表

//给作者详细信息表添加一条记录
models.AuthorDetail.objects.create(
birthday='2018-11-11',
telephone='18033336789',
addr='北京'
)


//给作者表添加一条记录
models.Author.objects.create(
name='小明',
age=1,

#方式1
#关联作者详细信息表中id为1的
ad=models.AuthorDetail.objects.get(id=1)

#方式2(常用)
ad_id = 1
)

iShot_2024-08-30_15.03.42

一对多,出版社和书籍表

models.Book.objects.create(
title='隔壁老王的故事',
publishDate='2011-01-01',
price='99',

#方式1
publishs=models.Publish.objects.get(id=1)

#方式2
publishs_id=2
)

iShot_2024-08-30_15.04.52

多对多,书籍表和作者表

//方式一
book_obj = models.Book.objects.get(id=1)
author1 =models.Author.objects.get(id=1)
author2 =models.Author.objects.get(id=2)

#向关联书籍表和作者表的第3张表中插入作者1和作者2,书籍id为1的书的作者有2个,分别为作者id为1的和作者id为2的
#方式1
book_obj.authors.add(author1,author2)
#方式2
book_obj.authors.add(*[author1,author2])


//方式二
book_obj = models.Book.objects.get(id=1)
#方式1
book_obj.authors.add(1,2)
#方式2
book.obj.authors.add(*[1,2])

查看关联作者表和书籍表的第3张表app01_book_authors,书籍id为1的书作者有2个

iShot_2024-08-30_15.06.49

5.2.3.2 删

一对一、一对多删除方式一样

models.Author.objects.get(id=1).delete()

删除了作者表中id1的记录,但是不会影响作者详细信息表中作者id1的记录

作者表的id作为外键关联作者详细信息表中的作者id,可以理解为我抱别人的大腿,删除我自己不影响别人,但是别人如果删除大腿会影响我

作者表是关联者,作者详细信息表是被关联者

多对多

//方式一	remove	移除
#找到书籍id为1的书
book_obj = models.Book.objects.get(id=1)

#删除书籍id为1的书的作者id为2和3
book_obj.authors.remove(2,3)


//方式二 clear 全部清空
#把书籍id为1的对应的作者全部删除
book_obj = models.Book.objects.get(id=1)
book_obj.authors.clear()

5.2.3.3 改

一对多和一对一和单表是一样的

#将书籍表中id为1的数据数名修改为python从入门到放弃并且出版社修改为出版社id为3的
book_obj = models.Book.objects.filter(id=1).update(
title='python从入门到放弃',

#方式1
publishs=models.Publish.objects.get(id=3)

#方式2
publishs_id=3

多对多

//set	先清空再追加,set中必须写字符串,删除多个在列表中填写多个数值
#把书籍id为1的作者全部改为2
book_obj = models.Book.objects.get(id=1)
book_obj.authors.set(['2',])

5.2.3.4 查

基于对象的跨表查询

一对一

作者表和作者详细信息表

//正向查询
#查询作者小明的住址
author_obj = models.Author.objects.get(name='小明')
# author_obj.ad直接拿到authordetail表中的那个记录对
print(author_obj.ad.addr)


//反向查询
#查询作者详细信息表中电话号以170开头的作者名字
authordetail_ojb = models.AuthorDetail.objects.filter(
telephone__startswith='170').first()



print(book_obj) #不加first打印结果 <QuerySet [<Book: Book object>]>
print(book_obj) #加first打印结果 Book object


#authordetail_ojb.author中的author是类名小写,直接定位到app01_author这张表
print(authordetail_ojb.author.name)

关于正向查询和反向查询的说明

iShot_2024-08-30_15.07.49

一对多

出版社表和书籍表

//正向查询	
#查询python从入门到放弃这本书的出版社
book_obj = models.Book.objects.filter(title='python从入门到放弃').first()
print(book_obj.publishs.name)


//反向查询
pub_obj = models.Publish.objects.get(name='南京出版社')

#pub_obj.book_set.all()中book_set是结果可能为多个,最后的all是拿到的书籍对象所有信息
books = pub_obj.book_set.all().values('title')
print(books)

#查询南京出版社中数名包含linux的书
books = pub_obj.book_set.filter(title__contains='linux').values('title')

多对多

书籍表和作者表

//正向查询
#查询书籍表中书名为linux从入门到放弃的作者
book_obj = models.Book.objects.filter(title='linux从入门到放弃').first()
authors = book_obj.authors.all().values('name')
print(authors)


//反向查询 反向查询中用字段存在的表的小写类名,这里为book
#查询作者小明写的书
xiaoming_obj = models.Author.objects.get(name='小明')
ret = xiaoming_obj.book_set.all().values('title')
print(ret)

查询书籍表中书名为linux从入门到放弃的作者

iShot_2024-08-30_15.09.22

基于双下划线的跨表查询 就是join连表查询

一对一

作者表与作者详细信息表

//查询作者小明的住址
#基于对象的跨表查询写法 正向查询
author_obj = models.Author.objects.get(name='小明')
#author_obj.ad直接拿到authordetail表中的那个记录对
print(author_obj.ad.addr)

#基于双下划线的跨表查询写法
#正向查询
ret = models.Author.objects.filter(name='小明').values('ad__addr')
print(ret)

#反向查询
ret = models.AuthorDetail.objects.filter(author__name='小明').values('addr')
print(ret)



正向查询中 关联字段__字段 就获得了关联表的数据
反向查询中 表名__字段

一对多

书籍表和出版社表

//查询东京出版社出版的书
#原生sql语句
select app01_book.title from app01_publish inner join app01_book on app01_publish.id = app01_book.publishs_id where app01_publish.name='东京出版社';

#正向查询
ret = models.Book.objects.filter(publishs__name='东京出版社').values('title')


#反向查询
ret = models.Publish.objects.filter(name='东京出版社').values('book__title')

多对多

书籍表和作者表

关联字段在书籍表中,从书籍表查询是正向查询

#查询一下小明写了哪些书
#正向查询 关联字段在book表中,现在要查询小明写了哪些书,当前是book表,表中没有作者名字的字段,因此需要先连表然后在取值
ret = models.Book.objects.filter(authors__name='小明').values('title')
print(ret)

#反向查询 现在要查询小明写了哪些书,当前是author表,表中有作者名字的字段,因此不需要先连表,已知字段在本表的就可以先写条件最后在连表
ret = models.Author.objects.filter(name='小明').values('book__title')
print(ret)



关联的方式,正向查询和反向查询都可以使用,关键在于查询的条件
关联字段__字段 就获得了关联表的数据
小写类名__字段

聚合查询

aggregate聚合查询,结果是普通字典,queryset的结束符

#导入相关模块
from django.db.models import Avg,Max,Min,Count,Sum

//查询书籍表中价格最高的
obj = models.Book.objects.all().aggregate(a=Max('price'))
print(obj)

结果:
{'a': Decimal('125.00')}

分组查询

//查询每个出版社出版的书的最高价格
#方式一
ret = models.Book.objects.values('publishs_id').annotate(m=Max('price'))
print(ret)

结果:
<QuerySet [{'publishs_id': 2, 'm': Decimal('125.00')}, {'publishs_id': 3, 'm': Decimal('99.00')}]>

方式一写法说明
values写在annotate前面,意思是以values括号内的字段作为分组的依据,annotate里面是你要做的统计结果,这样,返回结果为queryset类型数据,里面是字典{'publishs_id':1,'m':100}



#方式二
#从出版社表中查询,需要连接book表,因为book表中才有书名和价格,这么写表示直接以publish这张表的id进行分组
ret = models.Publish.objects.annotate(m=Max('book__price')).values('m','name')
print(ret)

结果:
<QuerySet [{'name': '东京出版社', 'm': Decimal('99.00')}, {'name': '南京出版社', 'm': Decimal('125.00')}, {'name': '北京出版社', 'm': None}]>

方式二写法说明
annotate直接写在了objects后面,意思是按照前面表的所有的数据(默认是id)作为分组依据,结果返回的是前面这个表的所有models对象(model对象中包含了每个对象自己的统计结果),在通过values来取值,取值时可以直接写字段和统计结果的别名,也是queryset类型,里面是字典{'m':100,'name':'东京出版社'}

F和Q查询

在book表中添加连个字段:点赞(dianzan)和评论(pinglun)

class Book(models.Model):
title = models.CharField(max_length=32)
publishDate=models.DateField()
dianzan = models.IntegerField(default=100)
pinglun = models.IntegerField(default=100)
price=models.DecimalField(max_digits=5,decimal_places=2)
publishs=models.ForeignKey(to="Publish",to_field="id",on_delete=models.CASCADE)

app01_book表中内容

iShot_2024-08-30_15.11.51

F查询 F查询用于一张表中的两个字段比较之后的符合条件的结果集

//查询book表中点赞数大于评论数的所有书籍
#low版写法
list1 = []
books = models.Book.objects.all()
for i in books:
if i.dianzan > i.comment:
list1.append(i)

#F查询写法
ret = models.Book.objects.filter(dianzan__gt=F('pinglun')).values('title')
print(ret)

结果:
<QuerySet [{'title': 'python从入门到放弃'}, {'title': 'linux从入门到放弃'}]>



//F还支持四则运算,例如将book表中的价格都上调50元models.Book.objects.all().update(
price=F('price')+50
)

Q查询 Q查询用于多个条件有与、或、非时的综合查询

与:& 或:| 非:~

orm执行原生sql语句

//方式一
ret = models.Book.objects.raw/branch('select * from app01_book')
for i in ret:
print(i.title)

结果:
会把表中所有书名打印出来



//方式二 django自带的连接通道(在项目下同名目录__init__.py中配置的pymysql)
from django.db import connection
import pymysql
def query(request):
conn = pymysql.connect()
cursor = connection.cursor()
cursor.execute('select * from app01_book;')
print(cursor.fetchall())



//方式三
def query(request):
conn = pymysql.connect(
host='127.0.0.1',
port=3306,
user='root',
password='PAPAlichencan!1',
database='orm02',
charset='utf8'

)
cursor = conn.cursor(pymysql.cursors.DictCursor)
cursor.execute('select * from app01_book;')
print(cursor.fetchall())

结果:
[{'id': 1, 'title': 'python从入门到放弃', 'publishDate': datetime.date(2011, 1, 1), 'price': Decimal('99.00'), 'publishs_id': 3, 'dianzan': 600, 'pinglun': 150}, {'id': 2, 'title': 'linux从入门到放弃', 'publishDate': datetime.date(2019, 12, 12), 'price': Decimal('99.90'), 'publishs_id': 2, 'dianzan': 500, 'pinglun': 300}, {'id': 3, 'title': '隔壁老王的故事', 'publishDate': datetime.date(2019, 11, 7), 'price': Decimal('125.00'), 'publishs_id': 2, 'dianzan': 100, 'pinglun': 100}, {'id': 4, 'title': 'linux安全指南', 'publishDate': datetime.date(2020, 11, 30), 'price': Decimal('66.00'), 'publishs_id': 2, 'dianzan': 300, 'pinglun': 2000}, {'id': 5, 'title': '奔跑的linux内核', 'publishDate': datetime.date(2019, 11, 1), 'price': Decimal('77.00'), 'publishs_id': 2, 'dianzan': 50, 'pinglun': 100}]

django外部脚本调用models数据库操作

使用场景:有的时候我们不想运行django项目只想执行一些视图中的逻辑,这个时候就用到了django外部脚本调用models数据库操作

操作方法:在django项目下新建一个xx.py文件,在xx.py文件中写入以下内容,然后直接运行这个xx.py文件就可以在不启动django项目的情况下而单独执行我们想执行的测试逻辑

#导入os模块
import os

if __name__ == '__main__':
#最后一个参数要写项目名.settings
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm02.settings")

#导入django
import django
django.setup()

#导入应用程序的models
from app01 import models
ret = models.Book.objects.all().values('title')
print(ret)

ORM事务和锁

models.Book.objects.select_for_update().filter(id=1)

事务

//方式1 全局配置
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'mxshop',
'HOST': '127.0.0.1',
'PORT': '3306',
'USER': 'root',
'PASSWORD': '123',
"ATOMIC_REQUESTS": True, #全局开启事务,绑定的是http请求响应整个过程当中的sql
}
}

方式1中,django项目中所有的视图逻辑全部加上了事物


//方式2: 视图函数加装饰器
from django.db import transaction
@transaction.atomic
def viewfunc(request):
# This code executes inside a transaction.
逻辑语句

方式2中,我们只想给某一个视图函数加事物,这个时候就用到了上述的方法


方式3: 上下文加装饰器
from django.db import transaction
def viewfunc(request):
# This code executes in autocommit mode (Django's default).
逻辑语句1

with transaction.atomic(): #保存点
# This code executes inside a transaction.
逻辑语句2

逻辑语句3


方式3中,我们只想给逻辑语句2添加事物,而其他语句不需要,这个时候就在逻辑语句2的上边写如上语句即可